Loading drivers/platform/msm/ipa/ipa_v3/ipa_client.c +174 −52 Original line number Diff line number Diff line Loading @@ -29,7 +29,8 @@ #define IPA_POLL_FOR_EMPTINESS_NUM 50 #define IPA_POLL_FOR_EMPTINESS_SLEEP_USEC 20 #define IPA_POLL_FOR_CHANNEL_STOP_NUM 10 #define IPA_CHANNEL_STOP_IN_PROC_TO_MSEC 5 #define IPA_CHANNEL_STOP_IN_PROC_SLEEP_USEC 200 /* xfer_rsc_idx should be 7 bits */ #define IPA_XFER_RSC_IDX_MAX 127 Loading Loading @@ -928,6 +929,12 @@ static int ipa3_reset_with_open_aggr_frame_wa(u32 clnt_hdl, goto start_chan_fail; } /* * Need to sleep for 1ms as required by H/W verified * sequence for resetting GSI channel */ msleep(IPA_POLL_AGGR_STATE_SLEEP_MSEC); /* Restore channels properties */ result = ipa3_restore_channel_properties(ep, &orig_chan_props, &orig_chan_scratch); Loading Loading @@ -957,7 +964,7 @@ int ipa3_reset_gsi_channel(u32 clnt_hdl) enum gsi_status gsi_res; int aggr_active_bitmap = 0; IPADBG("ipa3_reset_gsi_channel: entry\n"); IPADBG("entry\n"); if (clnt_hdl >= ipa3_ctx->ipa_num_pipes || ipa3_ctx->ep[clnt_hdl].valid == 0) { IPAERR("Bad parameter.\n"); Loading @@ -984,7 +991,11 @@ int ipa3_reset_gsi_channel(u32 clnt_hdl) } } /* Reset channel */ /* * Reset channel * If the reset called after stop, need to wait 1ms */ msleep(IPA_POLL_AGGR_STATE_SLEEP_MSEC); gsi_res = gsi_reset_channel(ep->gsi_chan_hdl); if (gsi_res != GSI_STATUS_SUCCESS) { IPAERR("Error resetting channel: %d\n", gsi_res); Loading @@ -996,7 +1007,7 @@ finish_reset: if (!ep->keep_ipa_awake) IPA_ACTIVE_CLIENTS_DEC_EP(ipa3_get_client_mapping(clnt_hdl)); IPADBG("ipa3_reset_gsi_channel: exit\n"); IPADBG("exit\n"); return 0; reset_chan_fail: Loading @@ -1011,7 +1022,7 @@ int ipa3_reset_gsi_event_ring(u32 clnt_hdl) int result = -EFAULT; enum gsi_status gsi_res; IPADBG("ipa3_reset_gsi_event_ring: entry\n"); IPADBG("entry\n"); if (clnt_hdl >= ipa3_ctx->ipa_num_pipes || ipa3_ctx->ep[clnt_hdl].valid == 0) { IPAERR("Bad parameter.\n"); Loading @@ -1033,7 +1044,7 @@ int ipa3_reset_gsi_event_ring(u32 clnt_hdl) if (!ep->keep_ipa_awake) IPA_ACTIVE_CLIENTS_DEC_EP(ipa3_get_client_mapping(clnt_hdl)); IPADBG("ipa3_reset_gsi_event_ring: exit\n"); IPADBG("exit\n"); return 0; reset_evt_fail: Loading Loading @@ -1380,52 +1391,160 @@ static int ipa3_disable_force_clear(u32 request_id) return 0; } /* Clocks should be voted before invoking this function */ static int ipa3_xdci_stop_gsi_channel(u32 clnt_hdl, bool *stop_in_proc) { int res; IPADBG("entry\n"); if (clnt_hdl >= ipa3_ctx->ipa_num_pipes || ipa3_ctx->ep[clnt_hdl].valid == 0 || !stop_in_proc) { IPAERR("Bad parameter.\n"); return -EINVAL; } res = ipa3_stop_gsi_channel(clnt_hdl); if (res != 0 && res != -GSI_STATUS_AGAIN && res != -GSI_STATUS_TIMED_OUT) { IPAERR("xDCI stop channel failed res=%d\n", res); return -EFAULT; } *stop_in_proc = res; IPADBG("xDCI channel is %s (result=%d)\n", res ? "STOP_IN_PROC/TimeOut" : "STOP", res); IPADBG("exit\n"); return 0; } /* Clocks should be voted before invoking this function */ static int ipa3_xdci_stop_gsi_ch_brute_force(u32 clnt_hdl, bool *stop_in_proc) { unsigned long jiffies_start; unsigned long jiffies_timeout = msecs_to_jiffies(IPA_CHANNEL_STOP_IN_PROC_TO_MSEC); int res; if (clnt_hdl >= ipa3_ctx->ipa_num_pipes || ipa3_ctx->ep[clnt_hdl].valid == 0 || !stop_in_proc) { IPAERR("Bad parameter.\n"); return -EINVAL; } jiffies_start = jiffies; while (1) { res = ipa3_xdci_stop_gsi_channel(clnt_hdl, stop_in_proc); if (res) { IPAERR("failed to stop xDCI channel hdl=%d\n", clnt_hdl); return res; } if (!*stop_in_proc) { IPADBG("xDCI channel STOP hdl=%d\n", clnt_hdl); return res; } /* * Give chance to the previous stop request to be accomplished * before the retry */ udelay(IPA_CHANNEL_STOP_IN_PROC_SLEEP_USEC); if (time_after(jiffies, jiffies_start + jiffies_timeout)) { IPADBG("timeout waiting for xDCI channel emptiness\n"); return res; } } } /* Clocks should be voted for before invoking this function */ static int ipa3_drain_ul_chan_data(struct ipa3_ep_context *ep, u32 qmi_req_id, u32 source_pipe_bitmask, bool should_force_clear) static int ipa3_stop_ul_chan_with_data_drain(u32 qmi_req_id, u32 source_pipe_bitmask, bool should_force_clear, u32 clnt_hdl) { int i; bool is_empty = false; int result; bool is_empty = false; int i; bool stop_in_proc; struct ipa3_ep_context *ep; if (clnt_hdl >= ipa3_ctx->ipa_num_pipes || ipa3_ctx->ep[clnt_hdl].valid == 0) { IPAERR("Bad parameter.\n"); return -EINVAL; } ep = &ipa3_ctx->ep[clnt_hdl]; /* first try to stop the channel */ result = ipa3_xdci_stop_gsi_ch_brute_force(clnt_hdl, &stop_in_proc); if (result) { IPAERR("fail to stop UL channel - hdl=%d clnt=%d\n", clnt_hdl, ep->client); goto exit; } if (!stop_in_proc) goto exit; /* if stop_in_proc, lets wait for emptiness */ for (i = 0; i < IPA_POLL_FOR_EMPTINESS_NUM; i++) { result = ipa3_is_xdci_channel_empty(ep, &is_empty); if (result) return -EFAULT; goto exit; if (is_empty) return 0; break; udelay(IPA_POLL_FOR_EMPTINESS_SLEEP_USEC); } /* In case of empty, lets try to stop the channel again */ if (is_empty) { result = ipa3_xdci_stop_gsi_ch_brute_force(clnt_hdl, &stop_in_proc); if (result) { IPAERR("fail to stop UL channel - hdl=%d clnt=%d\n", clnt_hdl, ep->client); goto exit; } if (!stop_in_proc) goto exit; } /* if still stop_in_proc or not empty, activate force clear */ if (should_force_clear) { result = ipa3_enable_force_clear(qmi_req_id, true, source_pipe_bitmask); if (result) return -EFAULT; goto exit; } /* with force clear, wait for emptiness */ for (i = 0; i < IPA_POLL_FOR_EMPTINESS_NUM; i++) { result = ipa3_is_xdci_channel_empty(ep, &is_empty); if (result) { result = -EFAULT; if (result) goto disable_force_clear_and_exit; if (is_empty) break; udelay(IPA_POLL_FOR_EMPTINESS_SLEEP_USEC); } if (is_empty) { result = 0; /* try to stop for the last time */ result = ipa3_xdci_stop_gsi_ch_brute_force(clnt_hdl, &stop_in_proc); if (result) { IPAERR("fail to stop UL channel - hdl=%d clnt=%d\n", clnt_hdl, ep->client); goto disable_force_clear_and_exit; } udelay(IPA_POLL_FOR_EMPTINESS_SLEEP_USEC); } result = stop_in_proc ? -EFAULT : 0; disable_force_clear_and_exit: if (should_force_clear) { if (should_force_clear) result = ipa3_disable_force_clear(qmi_req_id); if (result) return -EFAULT; } if (!result && !is_empty) { /* Not error and not not empty */ IPAERR("UL channel is not empty after draining it!\n"); BUG(); } exit: return result; } Loading @@ -1449,20 +1568,28 @@ int ipa3_xdci_disconnect(u32 clnt_hdl, bool should_force_clear, u32 qmi_req_id) ipa3_disable_data_path(clnt_hdl); /* Drain UL channel before stopping it */ if (!IPA_CLIENT_IS_CONS(ep->client)) { source_pipe_bitmask = 1 << ipa3_get_ep_mapping(ep->client); result = ipa3_drain_ul_chan_data(ep, qmi_req_id, source_pipe_bitmask, should_force_clear); if (result) IPAERR("Error draining UL channel data: %d\n", result); IPADBG("Stopping PROD channel - hdl=%d clnt=%d\n", clnt_hdl, ep->client); source_pipe_bitmask = 1 << ipa3_get_ep_mapping(ep->client); result = ipa3_stop_ul_chan_with_data_drain(qmi_req_id, source_pipe_bitmask, should_force_clear, clnt_hdl); if (result) { IPAERR("Fail to stop UL channel with data drain\n"); BUG(); goto stop_chan_fail; } } else { IPADBG("Stopping CONS channel - hdl=%d clnt=%d\n", clnt_hdl, ep->client); result = ipa3_stop_gsi_channel(clnt_hdl); if (result) { IPAERR("Error stopping channel: %d\n", result); IPAERR("Error stopping channel (CONS client): %d\n", result); goto stop_chan_fail; } } IPA_ACTIVE_CLIENTS_DEC_EP(ipa3_get_client_mapping(clnt_hdl)); IPADBG("exit\n"); Loading Loading @@ -1600,15 +1727,6 @@ int ipa3_xdci_suspend(u32 ul_clnt_hdl, u32 dl_clnt_hdl, goto disable_clk_and_exit; } /* Drain UL channel before stopping it */ if (!is_dpl && ul_data_pending) { source_pipe_bitmask = 1 << ipa3_get_ep_mapping(ul_ep->client); result = ipa3_drain_ul_chan_data(ul_ep, qmi_req_id, source_pipe_bitmask, should_force_clear); if (result) IPAERR("Error draining UL channel data: %d\n", result); } /* Suspend the DL/DPL EP */ memset(&ep_cfg_ctrl, 0 , sizeof(struct ipa_ep_cfg_ctrl)); ep_cfg_ctrl.ipa_ep_suspend = true; Loading @@ -1627,10 +1745,14 @@ int ipa3_xdci_suspend(u32 ul_clnt_hdl, u32 dl_clnt_hdl, goto unsuspend_dl_and_exit; } /* STOP UL channel */ if (!is_dpl) { result = ipa3_stop_gsi_channel(ul_clnt_hdl); source_pipe_bitmask = 1 << ipa3_get_ep_mapping(ul_ep->client); result = ipa3_stop_ul_chan_with_data_drain(qmi_req_id, source_pipe_bitmask, should_force_clear, ul_clnt_hdl); if (result) { IPAERR("Error stopping UL channel: %d\n", result); IPAERR("Error stopping UL channel: result = %d\n", result); goto unsuspend_dl_and_exit; } } Loading drivers/platform/msm/ipa/ipa_v3/ipa_usb.c +48 −55 Original line number Diff line number Diff line Loading @@ -127,7 +127,6 @@ struct ipa3_usb_context { struct ipa3_usb_teth_prot_context teth_prot_ctx[IPA_USB_MAX_TETH_PROT_SIZE]; int num_init_prot; /* without dpl */ enum ipa3_usb_teth_prot_state teth_bridge_state; struct teth_bridge_init_params teth_bridge_params; struct completion dev_ready_comp; u32 qmi_req_id; Loading Loading @@ -435,7 +434,6 @@ int ipa3_usb_init(void) ipa3_usb_ctx->teth_prot_ctx[i].state = IPA_USB_TETH_PROT_INVALID; ipa3_usb_ctx->num_init_prot = 0; ipa3_usb_ctx->teth_bridge_state = IPA_USB_TETH_PROT_INVALID; init_completion(&ipa3_usb_ctx->dev_ready_comp); ipa3_usb_ctx->qmi_req_id = 0; spin_lock_init(&ipa3_usb_ctx->state_lock); Loading Loading @@ -776,15 +774,11 @@ static int ipa3_usb_init_teth_bridge(void) { int result; if (ipa3_usb_ctx->teth_bridge_state != IPA_USB_TETH_PROT_INVALID) return 0; result = teth_bridge_init(&ipa3_usb_ctx->teth_bridge_params); if (result) { IPA_USB_ERR("Failed to initialize teth_bridge.\n"); return result; } ipa3_usb_ctx->teth_bridge_state = IPA_USB_TETH_PROT_INITIALIZED; return 0; } Loading Loading @@ -1375,9 +1369,6 @@ static int ipa3_usb_connect_teth_bridge( { int result; if (ipa3_usb_ctx->teth_bridge_state != IPA_USB_TETH_PROT_INITIALIZED) return 0; result = teth_bridge_connect(params); if (result) { IPA_USB_ERR("failed to connect teth_bridge (%s)\n", Loading @@ -1385,7 +1376,6 @@ static int ipa3_usb_connect_teth_bridge( "rmnet" : "mbim"); return result; } ipa3_usb_ctx->teth_bridge_state = IPA_USB_TETH_PROT_CONNECTED; return 0; } Loading @@ -1394,19 +1384,29 @@ static int ipa3_usb_connect_dpl(void) { int res = 0; /* Add DPL dependency to RM dependency graph */ /* * Add DPL dependency to RM dependency graph, first add_dependency call * is sync in order to make sure the IPA clocks are up before we * continue and notify the USB driver it may continue. */ res = ipa3_rm_add_dependency_sync(IPA_RM_RESOURCE_USB_DPL_DUMMY_PROD, IPA_RM_RESOURCE_Q6_CONS); if (res < 0) { IPA_USB_ERR("ipa3_rm_add_dependency_sync() failed.\n"); return res; } /* * this add_dependency call can't be sync since it will block until DPL * status is connected (which can happen only later in the flow), * the clocks are already up so the call doesn't need to block. */ res = ipa3_rm_add_dependency(IPA_RM_RESOURCE_Q6_PROD, IPA_RM_RESOURCE_USB_DPL_CONS); if (res < 0 && res != -EINPROGRESS) { IPA_USB_ERR("ipa3_rm_add_dependency() failed.\n"); return res; } res = ipa3_rm_add_dependency(IPA_RM_RESOURCE_USB_DPL_DUMMY_PROD, ipa3_rm_delete_dependency(IPA_RM_RESOURCE_USB_DPL_DUMMY_PROD, IPA_RM_RESOURCE_Q6_CONS); if (res < 0 && res != -EINPROGRESS) { IPA_USB_ERR("ipa3_rm_add_dependency() failed.\n"); ipa3_rm_delete_dependency(IPA_RM_RESOURCE_Q6_PROD, IPA_RM_RESOURCE_USB_DPL_CONS); return res; } Loading Loading @@ -1557,15 +1557,11 @@ static int ipa3_usb_disconnect_teth_bridge(void) { int result; if (ipa3_usb_ctx->teth_bridge_state != IPA_USB_TETH_PROT_CONNECTED) return 0; result = teth_bridge_disconnect(IPA_CLIENT_USB_PROD); if (result) { IPA_USB_ERR("failed to disconnect teth_bridge.\n"); return result; } ipa3_usb_ctx->teth_bridge_state = IPA_USB_TETH_PROT_INVALID; return 0; } Loading Loading @@ -1890,6 +1886,23 @@ int ipa3_usb_xdci_disconnect(u32 ul_clnt_hdl, u32 dl_clnt_hdl, goto bad_params; } spin_lock_irqsave(&ipa3_usb_ctx->state_lock, flags); if (ipa3_usb_ctx->ttype_ctx[ttype].state != IPA_USB_SUSPENDED) { spin_unlock_irqrestore(&ipa3_usb_ctx->state_lock, flags); /* Stop DL/DPL channel */ result = ipa3_xdci_disconnect(dl_clnt_hdl, false, -1); if (result) { IPA_USB_ERR("failed to disconnect DL/DPL channel.\n"); goto bad_params; } } else { spin_unlock_irqrestore(&ipa3_usb_ctx->state_lock, flags); memset(&holb_cfg, 0, sizeof(holb_cfg)); holb_cfg.en = IPA_HOLB_TMR_EN; holb_cfg.tmr_val = 0; ipa3_cfg_ep_holb(dl_clnt_hdl, &holb_cfg); } spin_lock_irqsave(&ipa3_usb_ctx->state_lock, flags); orig_state = ipa3_usb_ctx->ttype_ctx[ttype].state; if (!IPA3_USB_IS_TTYPE_DPL(ttype)) { Loading @@ -1915,21 +1928,18 @@ int ipa3_usb_xdci_disconnect(u32 ul_clnt_hdl, u32 dl_clnt_hdl, } else spin_unlock_irqrestore(&ipa3_usb_ctx->state_lock, flags); spin_lock_irqsave(&ipa3_usb_ctx->state_lock, flags); if (ipa3_usb_ctx->ttype_ctx[ttype].state != IPA_USB_SUSPENDED) { spin_unlock_irqrestore(&ipa3_usb_ctx->state_lock, flags); /* Stop DL/DPL channel */ result = ipa3_xdci_disconnect(dl_clnt_hdl, false, -1); /* Reset DL channel */ result = ipa3_reset_gsi_channel(dl_clnt_hdl); if (result) { IPA_USB_ERR("failed to disconnect DL/DPL channel.\n"); IPA_USB_ERR("failed to reset DL channel.\n"); goto bad_params; } } else { spin_unlock_irqrestore(&ipa3_usb_ctx->state_lock, flags); memset(&holb_cfg, 0, sizeof(holb_cfg)); holb_cfg.en = IPA_HOLB_TMR_EN; holb_cfg.tmr_val = 0; ipa3_cfg_ep_holb(dl_clnt_hdl, &holb_cfg); /* Reset DL event ring */ result = ipa3_reset_gsi_event_ring(dl_clnt_hdl); if (result) { IPA_USB_ERR("failed to reset DL event ring.\n"); goto bad_params; } if (!IPA3_USB_IS_TTYPE_DPL(ttype)) { Loading @@ -1948,20 +1958,6 @@ int ipa3_usb_xdci_disconnect(u32 ul_clnt_hdl, u32 dl_clnt_hdl, } } /* Reset DL channel */ result = ipa3_reset_gsi_channel(dl_clnt_hdl); if (result) { IPA_USB_ERR("failed to reset DL channel.\n"); goto bad_params; } /* Reset DL event ring */ result = ipa3_reset_gsi_event_ring(dl_clnt_hdl); if (result) { IPA_USB_ERR("failed to reset DL event ring.\n"); goto bad_params; } /* Change state to STOPPED */ if (!ipa3_usb_set_state(IPA_USB_STOPPED, false, ttype)) IPA_USB_ERR("failed to change state to stopped\n"); Loading Loading @@ -2061,9 +2057,6 @@ int ipa3_usb_deinit_teth_prot(enum ipa_usb_teth_prot teth_prot) result = -EINVAL; goto bad_params; } result = ipa3_usb_disconnect_teth_bridge(); if (result) goto bad_params; ipa3_usb_ctx->teth_prot_ctx[teth_prot].user_data = NULL; Loading drivers/platform/msm/ipa/ipa_v3/teth_bridge.c +35 −24 Original line number Diff line number Diff line Loading @@ -98,8 +98,6 @@ static void teth_bridge_ipa_cb(void *priv, enum ipa_dp_evt_type evt, */ int ipa3_teth_bridge_init(struct teth_bridge_init_params *params) { int res = 0; TETH_DBG_FUNC_ENTRY(); if (!params) { Loading @@ -112,28 +110,8 @@ int ipa3_teth_bridge_init(struct teth_bridge_init_params *params) params->private_data = NULL; params->skip_ep_cfg = true; /* Build dependency graph */ res = ipa3_rm_add_dependency(IPA_RM_RESOURCE_USB_PROD, IPA_RM_RESOURCE_Q6_CONS); if (res < 0 && res != -EINPROGRESS) { TETH_ERR("ipa3_rm_add_dependency() failed.\n"); goto bail; } res = ipa3_rm_add_dependency(IPA_RM_RESOURCE_Q6_PROD, IPA_RM_RESOURCE_USB_CONS); if (res < 0 && res != -EINPROGRESS) { ipa3_rm_delete_dependency(IPA_RM_RESOURCE_USB_PROD, IPA_RM_RESOURCE_Q6_CONS); TETH_ERR("ipa3_rm_add_dependency() failed.\n"); goto bail; } res = 0; goto bail; bail: TETH_DBG_FUNC_EXIT(); return res; return 0; } /** Loading Loading @@ -162,7 +140,40 @@ int ipa3_teth_bridge_disconnect(enum ipa_client_type client) */ int ipa3_teth_bridge_connect(struct teth_bridge_connect_params *connect_params) { return 0; int res = 0; TETH_DBG_FUNC_ENTRY(); /* Build the dependency graph, first add_dependency call is sync * in order to make sure the IPA clocks are up before we continue * and notify the USB driver it may continue. */ res = ipa3_rm_add_dependency_sync(IPA_RM_RESOURCE_USB_PROD, IPA_RM_RESOURCE_Q6_CONS); if (res < 0) { TETH_ERR("ipa3_rm_add_dependency() failed.\n"); goto bail; } /* this add_dependency call can't be sync since it will block until USB * status is connected (which can happen only after the tethering * bridge is connected), the clocks are already up so the call doesn't * need to block. */ res = ipa3_rm_add_dependency(IPA_RM_RESOURCE_Q6_PROD, IPA_RM_RESOURCE_USB_CONS); if (res < 0 && res != -EINPROGRESS) { ipa3_rm_delete_dependency(IPA_RM_RESOURCE_USB_PROD, IPA_RM_RESOURCE_Q6_CONS); TETH_ERR("ipa3_rm_add_dependency() failed.\n"); goto bail; } res = 0; bail: TETH_DBG_FUNC_EXIT(); return res; } static long ipa3_teth_bridge_ioctl(struct file *filp, Loading Loading
drivers/platform/msm/ipa/ipa_v3/ipa_client.c +174 −52 Original line number Diff line number Diff line Loading @@ -29,7 +29,8 @@ #define IPA_POLL_FOR_EMPTINESS_NUM 50 #define IPA_POLL_FOR_EMPTINESS_SLEEP_USEC 20 #define IPA_POLL_FOR_CHANNEL_STOP_NUM 10 #define IPA_CHANNEL_STOP_IN_PROC_TO_MSEC 5 #define IPA_CHANNEL_STOP_IN_PROC_SLEEP_USEC 200 /* xfer_rsc_idx should be 7 bits */ #define IPA_XFER_RSC_IDX_MAX 127 Loading Loading @@ -928,6 +929,12 @@ static int ipa3_reset_with_open_aggr_frame_wa(u32 clnt_hdl, goto start_chan_fail; } /* * Need to sleep for 1ms as required by H/W verified * sequence for resetting GSI channel */ msleep(IPA_POLL_AGGR_STATE_SLEEP_MSEC); /* Restore channels properties */ result = ipa3_restore_channel_properties(ep, &orig_chan_props, &orig_chan_scratch); Loading Loading @@ -957,7 +964,7 @@ int ipa3_reset_gsi_channel(u32 clnt_hdl) enum gsi_status gsi_res; int aggr_active_bitmap = 0; IPADBG("ipa3_reset_gsi_channel: entry\n"); IPADBG("entry\n"); if (clnt_hdl >= ipa3_ctx->ipa_num_pipes || ipa3_ctx->ep[clnt_hdl].valid == 0) { IPAERR("Bad parameter.\n"); Loading @@ -984,7 +991,11 @@ int ipa3_reset_gsi_channel(u32 clnt_hdl) } } /* Reset channel */ /* * Reset channel * If the reset called after stop, need to wait 1ms */ msleep(IPA_POLL_AGGR_STATE_SLEEP_MSEC); gsi_res = gsi_reset_channel(ep->gsi_chan_hdl); if (gsi_res != GSI_STATUS_SUCCESS) { IPAERR("Error resetting channel: %d\n", gsi_res); Loading @@ -996,7 +1007,7 @@ finish_reset: if (!ep->keep_ipa_awake) IPA_ACTIVE_CLIENTS_DEC_EP(ipa3_get_client_mapping(clnt_hdl)); IPADBG("ipa3_reset_gsi_channel: exit\n"); IPADBG("exit\n"); return 0; reset_chan_fail: Loading @@ -1011,7 +1022,7 @@ int ipa3_reset_gsi_event_ring(u32 clnt_hdl) int result = -EFAULT; enum gsi_status gsi_res; IPADBG("ipa3_reset_gsi_event_ring: entry\n"); IPADBG("entry\n"); if (clnt_hdl >= ipa3_ctx->ipa_num_pipes || ipa3_ctx->ep[clnt_hdl].valid == 0) { IPAERR("Bad parameter.\n"); Loading @@ -1033,7 +1044,7 @@ int ipa3_reset_gsi_event_ring(u32 clnt_hdl) if (!ep->keep_ipa_awake) IPA_ACTIVE_CLIENTS_DEC_EP(ipa3_get_client_mapping(clnt_hdl)); IPADBG("ipa3_reset_gsi_event_ring: exit\n"); IPADBG("exit\n"); return 0; reset_evt_fail: Loading Loading @@ -1380,52 +1391,160 @@ static int ipa3_disable_force_clear(u32 request_id) return 0; } /* Clocks should be voted before invoking this function */ static int ipa3_xdci_stop_gsi_channel(u32 clnt_hdl, bool *stop_in_proc) { int res; IPADBG("entry\n"); if (clnt_hdl >= ipa3_ctx->ipa_num_pipes || ipa3_ctx->ep[clnt_hdl].valid == 0 || !stop_in_proc) { IPAERR("Bad parameter.\n"); return -EINVAL; } res = ipa3_stop_gsi_channel(clnt_hdl); if (res != 0 && res != -GSI_STATUS_AGAIN && res != -GSI_STATUS_TIMED_OUT) { IPAERR("xDCI stop channel failed res=%d\n", res); return -EFAULT; } *stop_in_proc = res; IPADBG("xDCI channel is %s (result=%d)\n", res ? "STOP_IN_PROC/TimeOut" : "STOP", res); IPADBG("exit\n"); return 0; } /* Clocks should be voted before invoking this function */ static int ipa3_xdci_stop_gsi_ch_brute_force(u32 clnt_hdl, bool *stop_in_proc) { unsigned long jiffies_start; unsigned long jiffies_timeout = msecs_to_jiffies(IPA_CHANNEL_STOP_IN_PROC_TO_MSEC); int res; if (clnt_hdl >= ipa3_ctx->ipa_num_pipes || ipa3_ctx->ep[clnt_hdl].valid == 0 || !stop_in_proc) { IPAERR("Bad parameter.\n"); return -EINVAL; } jiffies_start = jiffies; while (1) { res = ipa3_xdci_stop_gsi_channel(clnt_hdl, stop_in_proc); if (res) { IPAERR("failed to stop xDCI channel hdl=%d\n", clnt_hdl); return res; } if (!*stop_in_proc) { IPADBG("xDCI channel STOP hdl=%d\n", clnt_hdl); return res; } /* * Give chance to the previous stop request to be accomplished * before the retry */ udelay(IPA_CHANNEL_STOP_IN_PROC_SLEEP_USEC); if (time_after(jiffies, jiffies_start + jiffies_timeout)) { IPADBG("timeout waiting for xDCI channel emptiness\n"); return res; } } } /* Clocks should be voted for before invoking this function */ static int ipa3_drain_ul_chan_data(struct ipa3_ep_context *ep, u32 qmi_req_id, u32 source_pipe_bitmask, bool should_force_clear) static int ipa3_stop_ul_chan_with_data_drain(u32 qmi_req_id, u32 source_pipe_bitmask, bool should_force_clear, u32 clnt_hdl) { int i; bool is_empty = false; int result; bool is_empty = false; int i; bool stop_in_proc; struct ipa3_ep_context *ep; if (clnt_hdl >= ipa3_ctx->ipa_num_pipes || ipa3_ctx->ep[clnt_hdl].valid == 0) { IPAERR("Bad parameter.\n"); return -EINVAL; } ep = &ipa3_ctx->ep[clnt_hdl]; /* first try to stop the channel */ result = ipa3_xdci_stop_gsi_ch_brute_force(clnt_hdl, &stop_in_proc); if (result) { IPAERR("fail to stop UL channel - hdl=%d clnt=%d\n", clnt_hdl, ep->client); goto exit; } if (!stop_in_proc) goto exit; /* if stop_in_proc, lets wait for emptiness */ for (i = 0; i < IPA_POLL_FOR_EMPTINESS_NUM; i++) { result = ipa3_is_xdci_channel_empty(ep, &is_empty); if (result) return -EFAULT; goto exit; if (is_empty) return 0; break; udelay(IPA_POLL_FOR_EMPTINESS_SLEEP_USEC); } /* In case of empty, lets try to stop the channel again */ if (is_empty) { result = ipa3_xdci_stop_gsi_ch_brute_force(clnt_hdl, &stop_in_proc); if (result) { IPAERR("fail to stop UL channel - hdl=%d clnt=%d\n", clnt_hdl, ep->client); goto exit; } if (!stop_in_proc) goto exit; } /* if still stop_in_proc or not empty, activate force clear */ if (should_force_clear) { result = ipa3_enable_force_clear(qmi_req_id, true, source_pipe_bitmask); if (result) return -EFAULT; goto exit; } /* with force clear, wait for emptiness */ for (i = 0; i < IPA_POLL_FOR_EMPTINESS_NUM; i++) { result = ipa3_is_xdci_channel_empty(ep, &is_empty); if (result) { result = -EFAULT; if (result) goto disable_force_clear_and_exit; if (is_empty) break; udelay(IPA_POLL_FOR_EMPTINESS_SLEEP_USEC); } if (is_empty) { result = 0; /* try to stop for the last time */ result = ipa3_xdci_stop_gsi_ch_brute_force(clnt_hdl, &stop_in_proc); if (result) { IPAERR("fail to stop UL channel - hdl=%d clnt=%d\n", clnt_hdl, ep->client); goto disable_force_clear_and_exit; } udelay(IPA_POLL_FOR_EMPTINESS_SLEEP_USEC); } result = stop_in_proc ? -EFAULT : 0; disable_force_clear_and_exit: if (should_force_clear) { if (should_force_clear) result = ipa3_disable_force_clear(qmi_req_id); if (result) return -EFAULT; } if (!result && !is_empty) { /* Not error and not not empty */ IPAERR("UL channel is not empty after draining it!\n"); BUG(); } exit: return result; } Loading @@ -1449,20 +1568,28 @@ int ipa3_xdci_disconnect(u32 clnt_hdl, bool should_force_clear, u32 qmi_req_id) ipa3_disable_data_path(clnt_hdl); /* Drain UL channel before stopping it */ if (!IPA_CLIENT_IS_CONS(ep->client)) { source_pipe_bitmask = 1 << ipa3_get_ep_mapping(ep->client); result = ipa3_drain_ul_chan_data(ep, qmi_req_id, source_pipe_bitmask, should_force_clear); if (result) IPAERR("Error draining UL channel data: %d\n", result); IPADBG("Stopping PROD channel - hdl=%d clnt=%d\n", clnt_hdl, ep->client); source_pipe_bitmask = 1 << ipa3_get_ep_mapping(ep->client); result = ipa3_stop_ul_chan_with_data_drain(qmi_req_id, source_pipe_bitmask, should_force_clear, clnt_hdl); if (result) { IPAERR("Fail to stop UL channel with data drain\n"); BUG(); goto stop_chan_fail; } } else { IPADBG("Stopping CONS channel - hdl=%d clnt=%d\n", clnt_hdl, ep->client); result = ipa3_stop_gsi_channel(clnt_hdl); if (result) { IPAERR("Error stopping channel: %d\n", result); IPAERR("Error stopping channel (CONS client): %d\n", result); goto stop_chan_fail; } } IPA_ACTIVE_CLIENTS_DEC_EP(ipa3_get_client_mapping(clnt_hdl)); IPADBG("exit\n"); Loading Loading @@ -1600,15 +1727,6 @@ int ipa3_xdci_suspend(u32 ul_clnt_hdl, u32 dl_clnt_hdl, goto disable_clk_and_exit; } /* Drain UL channel before stopping it */ if (!is_dpl && ul_data_pending) { source_pipe_bitmask = 1 << ipa3_get_ep_mapping(ul_ep->client); result = ipa3_drain_ul_chan_data(ul_ep, qmi_req_id, source_pipe_bitmask, should_force_clear); if (result) IPAERR("Error draining UL channel data: %d\n", result); } /* Suspend the DL/DPL EP */ memset(&ep_cfg_ctrl, 0 , sizeof(struct ipa_ep_cfg_ctrl)); ep_cfg_ctrl.ipa_ep_suspend = true; Loading @@ -1627,10 +1745,14 @@ int ipa3_xdci_suspend(u32 ul_clnt_hdl, u32 dl_clnt_hdl, goto unsuspend_dl_and_exit; } /* STOP UL channel */ if (!is_dpl) { result = ipa3_stop_gsi_channel(ul_clnt_hdl); source_pipe_bitmask = 1 << ipa3_get_ep_mapping(ul_ep->client); result = ipa3_stop_ul_chan_with_data_drain(qmi_req_id, source_pipe_bitmask, should_force_clear, ul_clnt_hdl); if (result) { IPAERR("Error stopping UL channel: %d\n", result); IPAERR("Error stopping UL channel: result = %d\n", result); goto unsuspend_dl_and_exit; } } Loading
drivers/platform/msm/ipa/ipa_v3/ipa_usb.c +48 −55 Original line number Diff line number Diff line Loading @@ -127,7 +127,6 @@ struct ipa3_usb_context { struct ipa3_usb_teth_prot_context teth_prot_ctx[IPA_USB_MAX_TETH_PROT_SIZE]; int num_init_prot; /* without dpl */ enum ipa3_usb_teth_prot_state teth_bridge_state; struct teth_bridge_init_params teth_bridge_params; struct completion dev_ready_comp; u32 qmi_req_id; Loading Loading @@ -435,7 +434,6 @@ int ipa3_usb_init(void) ipa3_usb_ctx->teth_prot_ctx[i].state = IPA_USB_TETH_PROT_INVALID; ipa3_usb_ctx->num_init_prot = 0; ipa3_usb_ctx->teth_bridge_state = IPA_USB_TETH_PROT_INVALID; init_completion(&ipa3_usb_ctx->dev_ready_comp); ipa3_usb_ctx->qmi_req_id = 0; spin_lock_init(&ipa3_usb_ctx->state_lock); Loading Loading @@ -776,15 +774,11 @@ static int ipa3_usb_init_teth_bridge(void) { int result; if (ipa3_usb_ctx->teth_bridge_state != IPA_USB_TETH_PROT_INVALID) return 0; result = teth_bridge_init(&ipa3_usb_ctx->teth_bridge_params); if (result) { IPA_USB_ERR("Failed to initialize teth_bridge.\n"); return result; } ipa3_usb_ctx->teth_bridge_state = IPA_USB_TETH_PROT_INITIALIZED; return 0; } Loading Loading @@ -1375,9 +1369,6 @@ static int ipa3_usb_connect_teth_bridge( { int result; if (ipa3_usb_ctx->teth_bridge_state != IPA_USB_TETH_PROT_INITIALIZED) return 0; result = teth_bridge_connect(params); if (result) { IPA_USB_ERR("failed to connect teth_bridge (%s)\n", Loading @@ -1385,7 +1376,6 @@ static int ipa3_usb_connect_teth_bridge( "rmnet" : "mbim"); return result; } ipa3_usb_ctx->teth_bridge_state = IPA_USB_TETH_PROT_CONNECTED; return 0; } Loading @@ -1394,19 +1384,29 @@ static int ipa3_usb_connect_dpl(void) { int res = 0; /* Add DPL dependency to RM dependency graph */ /* * Add DPL dependency to RM dependency graph, first add_dependency call * is sync in order to make sure the IPA clocks are up before we * continue and notify the USB driver it may continue. */ res = ipa3_rm_add_dependency_sync(IPA_RM_RESOURCE_USB_DPL_DUMMY_PROD, IPA_RM_RESOURCE_Q6_CONS); if (res < 0) { IPA_USB_ERR("ipa3_rm_add_dependency_sync() failed.\n"); return res; } /* * this add_dependency call can't be sync since it will block until DPL * status is connected (which can happen only later in the flow), * the clocks are already up so the call doesn't need to block. */ res = ipa3_rm_add_dependency(IPA_RM_RESOURCE_Q6_PROD, IPA_RM_RESOURCE_USB_DPL_CONS); if (res < 0 && res != -EINPROGRESS) { IPA_USB_ERR("ipa3_rm_add_dependency() failed.\n"); return res; } res = ipa3_rm_add_dependency(IPA_RM_RESOURCE_USB_DPL_DUMMY_PROD, ipa3_rm_delete_dependency(IPA_RM_RESOURCE_USB_DPL_DUMMY_PROD, IPA_RM_RESOURCE_Q6_CONS); if (res < 0 && res != -EINPROGRESS) { IPA_USB_ERR("ipa3_rm_add_dependency() failed.\n"); ipa3_rm_delete_dependency(IPA_RM_RESOURCE_Q6_PROD, IPA_RM_RESOURCE_USB_DPL_CONS); return res; } Loading Loading @@ -1557,15 +1557,11 @@ static int ipa3_usb_disconnect_teth_bridge(void) { int result; if (ipa3_usb_ctx->teth_bridge_state != IPA_USB_TETH_PROT_CONNECTED) return 0; result = teth_bridge_disconnect(IPA_CLIENT_USB_PROD); if (result) { IPA_USB_ERR("failed to disconnect teth_bridge.\n"); return result; } ipa3_usb_ctx->teth_bridge_state = IPA_USB_TETH_PROT_INVALID; return 0; } Loading Loading @@ -1890,6 +1886,23 @@ int ipa3_usb_xdci_disconnect(u32 ul_clnt_hdl, u32 dl_clnt_hdl, goto bad_params; } spin_lock_irqsave(&ipa3_usb_ctx->state_lock, flags); if (ipa3_usb_ctx->ttype_ctx[ttype].state != IPA_USB_SUSPENDED) { spin_unlock_irqrestore(&ipa3_usb_ctx->state_lock, flags); /* Stop DL/DPL channel */ result = ipa3_xdci_disconnect(dl_clnt_hdl, false, -1); if (result) { IPA_USB_ERR("failed to disconnect DL/DPL channel.\n"); goto bad_params; } } else { spin_unlock_irqrestore(&ipa3_usb_ctx->state_lock, flags); memset(&holb_cfg, 0, sizeof(holb_cfg)); holb_cfg.en = IPA_HOLB_TMR_EN; holb_cfg.tmr_val = 0; ipa3_cfg_ep_holb(dl_clnt_hdl, &holb_cfg); } spin_lock_irqsave(&ipa3_usb_ctx->state_lock, flags); orig_state = ipa3_usb_ctx->ttype_ctx[ttype].state; if (!IPA3_USB_IS_TTYPE_DPL(ttype)) { Loading @@ -1915,21 +1928,18 @@ int ipa3_usb_xdci_disconnect(u32 ul_clnt_hdl, u32 dl_clnt_hdl, } else spin_unlock_irqrestore(&ipa3_usb_ctx->state_lock, flags); spin_lock_irqsave(&ipa3_usb_ctx->state_lock, flags); if (ipa3_usb_ctx->ttype_ctx[ttype].state != IPA_USB_SUSPENDED) { spin_unlock_irqrestore(&ipa3_usb_ctx->state_lock, flags); /* Stop DL/DPL channel */ result = ipa3_xdci_disconnect(dl_clnt_hdl, false, -1); /* Reset DL channel */ result = ipa3_reset_gsi_channel(dl_clnt_hdl); if (result) { IPA_USB_ERR("failed to disconnect DL/DPL channel.\n"); IPA_USB_ERR("failed to reset DL channel.\n"); goto bad_params; } } else { spin_unlock_irqrestore(&ipa3_usb_ctx->state_lock, flags); memset(&holb_cfg, 0, sizeof(holb_cfg)); holb_cfg.en = IPA_HOLB_TMR_EN; holb_cfg.tmr_val = 0; ipa3_cfg_ep_holb(dl_clnt_hdl, &holb_cfg); /* Reset DL event ring */ result = ipa3_reset_gsi_event_ring(dl_clnt_hdl); if (result) { IPA_USB_ERR("failed to reset DL event ring.\n"); goto bad_params; } if (!IPA3_USB_IS_TTYPE_DPL(ttype)) { Loading @@ -1948,20 +1958,6 @@ int ipa3_usb_xdci_disconnect(u32 ul_clnt_hdl, u32 dl_clnt_hdl, } } /* Reset DL channel */ result = ipa3_reset_gsi_channel(dl_clnt_hdl); if (result) { IPA_USB_ERR("failed to reset DL channel.\n"); goto bad_params; } /* Reset DL event ring */ result = ipa3_reset_gsi_event_ring(dl_clnt_hdl); if (result) { IPA_USB_ERR("failed to reset DL event ring.\n"); goto bad_params; } /* Change state to STOPPED */ if (!ipa3_usb_set_state(IPA_USB_STOPPED, false, ttype)) IPA_USB_ERR("failed to change state to stopped\n"); Loading Loading @@ -2061,9 +2057,6 @@ int ipa3_usb_deinit_teth_prot(enum ipa_usb_teth_prot teth_prot) result = -EINVAL; goto bad_params; } result = ipa3_usb_disconnect_teth_bridge(); if (result) goto bad_params; ipa3_usb_ctx->teth_prot_ctx[teth_prot].user_data = NULL; Loading
drivers/platform/msm/ipa/ipa_v3/teth_bridge.c +35 −24 Original line number Diff line number Diff line Loading @@ -98,8 +98,6 @@ static void teth_bridge_ipa_cb(void *priv, enum ipa_dp_evt_type evt, */ int ipa3_teth_bridge_init(struct teth_bridge_init_params *params) { int res = 0; TETH_DBG_FUNC_ENTRY(); if (!params) { Loading @@ -112,28 +110,8 @@ int ipa3_teth_bridge_init(struct teth_bridge_init_params *params) params->private_data = NULL; params->skip_ep_cfg = true; /* Build dependency graph */ res = ipa3_rm_add_dependency(IPA_RM_RESOURCE_USB_PROD, IPA_RM_RESOURCE_Q6_CONS); if (res < 0 && res != -EINPROGRESS) { TETH_ERR("ipa3_rm_add_dependency() failed.\n"); goto bail; } res = ipa3_rm_add_dependency(IPA_RM_RESOURCE_Q6_PROD, IPA_RM_RESOURCE_USB_CONS); if (res < 0 && res != -EINPROGRESS) { ipa3_rm_delete_dependency(IPA_RM_RESOURCE_USB_PROD, IPA_RM_RESOURCE_Q6_CONS); TETH_ERR("ipa3_rm_add_dependency() failed.\n"); goto bail; } res = 0; goto bail; bail: TETH_DBG_FUNC_EXIT(); return res; return 0; } /** Loading Loading @@ -162,7 +140,40 @@ int ipa3_teth_bridge_disconnect(enum ipa_client_type client) */ int ipa3_teth_bridge_connect(struct teth_bridge_connect_params *connect_params) { return 0; int res = 0; TETH_DBG_FUNC_ENTRY(); /* Build the dependency graph, first add_dependency call is sync * in order to make sure the IPA clocks are up before we continue * and notify the USB driver it may continue. */ res = ipa3_rm_add_dependency_sync(IPA_RM_RESOURCE_USB_PROD, IPA_RM_RESOURCE_Q6_CONS); if (res < 0) { TETH_ERR("ipa3_rm_add_dependency() failed.\n"); goto bail; } /* this add_dependency call can't be sync since it will block until USB * status is connected (which can happen only after the tethering * bridge is connected), the clocks are already up so the call doesn't * need to block. */ res = ipa3_rm_add_dependency(IPA_RM_RESOURCE_Q6_PROD, IPA_RM_RESOURCE_USB_CONS); if (res < 0 && res != -EINPROGRESS) { ipa3_rm_delete_dependency(IPA_RM_RESOURCE_USB_PROD, IPA_RM_RESOURCE_Q6_CONS); TETH_ERR("ipa3_rm_add_dependency() failed.\n"); goto bail; } res = 0; bail: TETH_DBG_FUNC_EXIT(); return res; } static long ipa3_teth_bridge_ioctl(struct file *filp, Loading