Kaynağa Gözat

msm: ipa4: Enhanced flow control changes

Adding changes to support enhanced flow control.

Change-Id: Id656319703a27f5073c6fd129fb4202a37b49aad
Signed-off-by: Ashok Vuyyuru <[email protected]>
Ashok Vuyyuru 5 yıl önce
ebeveyn
işleme
7aa1dc1510

+ 158 - 0
drivers/platform/msm/gsi/gsi.c

@@ -1477,6 +1477,9 @@ int gsi_register_device(struct gsi_per_props *props, unsigned long *dev_hdl)
 	if (gsi_ctx->per.ver >= GSI_VER_1_2)
 		gsihal_write_reg_n(GSI_EE_n_ERROR_LOG, gsi_ctx->per.ee, 0);
 
+	/* Reset to zero scratch_1 register*/
+	gsihal_write_reg_n(GSI_EE_n_CNTXT_SCRATCH_1, gsi_ctx->per.ee, 0);
+
 	if (running_emulation) {
 		/*
 		 * Set up the emulator's interrupt controller...
@@ -4651,6 +4654,161 @@ free_lock:
 }
 EXPORT_SYMBOL(gsi_enable_flow_control_ee);
 
+int gsi_flow_control_ee(unsigned int chan_idx, unsigned int ee,
+				bool enable, bool prmy_scnd_fc, int *code)
+{
+	struct gsihal_reg_gsi_ee_generic_cmd cmd;
+	enum gsi_generic_ee_cmd_opcode op = enable ?
+					GSI_GEN_EE_CMD_ENABLE_FLOW_CHANNEL :
+					GSI_GEN_EE_CMD_DISABLE_FLOW_CHANNEL;
+	int res;
+
+	if (!gsi_ctx) {
+		pr_err("%s:%d gsi context not allocated\n", __func__, __LINE__);
+		return -GSI_STATUS_NODEV;
+	}
+
+	if (chan_idx >= gsi_ctx->max_ch || !code) {
+		GSIERR("bad params chan_idx=%d\n", chan_idx);
+		return -GSI_STATUS_INVALID_PARAMS;
+	}
+
+	mutex_lock(&gsi_ctx->mlock);
+	__gsi_config_glob_irq(gsi_ctx->per.ee,
+			gsihal_get_glob_irq_en_gp_int1_mask(), ~0);
+	reinit_completion(&gsi_ctx->gen_ee_cmd_compl);
+
+	/* invalidate the response */
+	gsi_ctx->scratch.word0.val = gsihal_read_reg_n(GSI_EE_n_CNTXT_SCRATCH_0,
+                gsi_ctx->per.ee);
+
+	gsi_ctx->scratch.word0.s.generic_ee_cmd_return_code = 0;
+	gsihal_write_reg_n(GSI_EE_n_CNTXT_SCRATCH_0,
+                gsi_ctx->per.ee, gsi_ctx->scratch.word0.val);
+
+	gsi_ctx->gen_ee_cmd_dbg.flow_ctrl_channel++;
+	cmd.opcode = op;
+	cmd.virt_chan_idx = chan_idx;
+	cmd.ee = ee;
+	cmd.prmy_scnd_fc = prmy_scnd_fc;
+	gsihal_write_reg_n_fields(
+		GSI_EE_n_GSI_EE_GENERIC_CMD, gsi_ctx->per.ee, &cmd);
+
+	res = wait_for_completion_timeout(&gsi_ctx->gen_ee_cmd_compl,
+		msecs_to_jiffies(GSI_CMD_TIMEOUT));
+	if (res == 0) {
+		GSIERR("chan_idx=%u ee=%u timed out\n", chan_idx, ee);
+		res = -GSI_STATUS_TIMED_OUT;
+		GSI_ASSERT();
+		goto free_lock;
+	}
+
+	gsi_ctx->scratch.word0.val = gsihal_read_reg_n(GSI_EE_n_CNTXT_SCRATCH_0,
+					gsi_ctx->per.ee);
+
+	if (gsi_ctx->scratch.word0.s.generic_ee_cmd_return_code ==
+		GSI_GEN_EE_CMD_RETURN_CODE_CHANNEL_NOT_RUNNING) {
+		GSIDBG("chan_idx=%u ee=%u not in correct state\n",
+							chan_idx, ee);
+		*code = GSI_GEN_EE_CMD_RETURN_CODE_CHANNEL_NOT_RUNNING;
+		res = -GSI_STATUS_RES_ALLOC_FAILURE;
+		goto free_lock;
+	} else if (gsi_ctx->scratch.word0.s.generic_ee_cmd_return_code ==
+			GSI_GEN_EE_CMD_RETURN_CODE_INCORRECT_CHANNEL_TYPE) {
+		GSIERR("chan_idx=%u ee=%u not in correct state\n",
+				chan_idx, ee);
+		GSI_ASSERT();
+	} else if (gsi_ctx->scratch.word0.s.generic_ee_cmd_return_code ==
+			GSI_GEN_EE_CMD_RETURN_CODE_INCORRECT_CHANNEL_INDEX) {
+		GSIERR("Channel ID = %u ee = %u not allocated\n", chan_idx, ee);
+	}
+
+	if (gsi_ctx->scratch.word0.s.generic_ee_cmd_return_code == 0) {
+		GSIERR("No response received\n");
+		res = -GSI_STATUS_ERROR;
+		goto free_lock;
+	}
+
+	*code = gsi_ctx->scratch.word0.s.generic_ee_cmd_return_code;
+	res = GSI_STATUS_SUCCESS;
+free_lock:
+	__gsi_config_glob_irq(gsi_ctx->per.ee,
+			gsihal_get_glob_irq_en_gp_int1_mask(), 0);
+	mutex_unlock(&gsi_ctx->mlock);
+
+	return res;
+}
+EXPORT_SYMBOL(gsi_flow_control_ee);
+
+int gsi_query_flow_control_state_ee(unsigned int chan_idx, unsigned int ee,
+						bool prmy_scnd_fc, int *code)
+{
+	struct gsihal_reg_gsi_ee_generic_cmd cmd;
+	enum gsi_generic_ee_cmd_opcode op = GSI_GEN_EE_CMD_QUERY_FLOW_CHANNEL;
+	int res;
+
+	if (!gsi_ctx) {
+		pr_err("%s:%d gsi context not allocated\n", __func__, __LINE__);
+		return -GSI_STATUS_NODEV;
+	}
+
+	if (chan_idx >= gsi_ctx->max_ch || !code) {
+		GSIERR("bad params chan_idx=%d\n", chan_idx);
+		return -GSI_STATUS_INVALID_PARAMS;
+	}
+
+	mutex_lock(&gsi_ctx->mlock);
+	__gsi_config_glob_irq(gsi_ctx->per.ee,
+			gsihal_get_glob_irq_en_gp_int1_mask(), ~0);
+	reinit_completion(&gsi_ctx->gen_ee_cmd_compl);
+
+	/* invalidate the response */
+	gsi_ctx->scratch.word0.val = gsihal_read_reg_n(GSI_EE_n_CNTXT_SCRATCH_0,
+							gsi_ctx->per.ee);
+	gsi_ctx->scratch.word0.s.generic_ee_cmd_return_code = 0;
+	gsihal_write_reg_n(GSI_EE_n_CNTXT_SCRATCH_0,
+                gsi_ctx->per.ee, gsi_ctx->scratch.word0.val);
+
+	gsi_ctx->gen_ee_cmd_dbg.flow_ctrl_channel++;
+	cmd.opcode = op;
+	cmd.virt_chan_idx = chan_idx;
+	cmd.ee = ee;
+	cmd.prmy_scnd_fc = prmy_scnd_fc;
+	gsihal_write_reg_n_fields(
+			GSI_EE_n_GSI_EE_GENERIC_CMD, gsi_ctx->per.ee, &cmd);
+
+	res = wait_for_completion_timeout(&gsi_ctx->gen_ee_cmd_compl,
+		msecs_to_jiffies(GSI_CMD_TIMEOUT));
+	if (res == 0) {
+		GSIERR("chan_idx=%u ee=%u timed out\n", chan_idx, ee);
+		res = -GSI_STATUS_TIMED_OUT;
+		goto free_lock;
+	}
+
+	gsi_ctx->scratch.word0.val = gsihal_read_reg_n(GSI_EE_n_CNTXT_SCRATCH_0,
+					gsi_ctx->per.ee);
+
+	*code = gsi_ctx->scratch.word0.s.generic_ee_cmd_return_val;
+
+	if (prmy_scnd_fc)
+		res = (gsi_ctx->scratch.word0.s.generic_ee_cmd_return_val ==
+		GSI_GEN_EE_CMD_RETURN_VAL_FLOW_CONTROL_SECONDARY)?
+				GSI_STATUS_SUCCESS:-GSI_STATUS_ERROR;
+	else
+		res = (gsi_ctx->scratch.word0.s.generic_ee_cmd_return_val ==
+		GSI_GEN_EE_CMD_RETURN_VAL_FLOW_CONTROL_PRIMARY)?
+				GSI_STATUS_SUCCESS:-GSI_STATUS_ERROR;
+
+free_lock:
+	__gsi_config_glob_irq(gsi_ctx->per.ee,
+			gsihal_get_glob_irq_en_gp_int1_mask(), 0);
+	mutex_unlock(&gsi_ctx->mlock);
+
+	return res;
+}
+EXPORT_SYMBOL(gsi_query_flow_control_state_ee);
+
+
 int gsi_map_virtual_ch_to_per_ep(u32 ee, u32 chan_num, u32 per_ep_index)
 {
 	if (!gsi_ctx) {

+ 13 - 1
drivers/platform/msm/gsi/gsi.h

@@ -1354,7 +1354,9 @@ struct gsi_ee_scratch {
 			uint32_t inter_ee_cmd_return_code:3;
 			uint32_t resvd1:2;
 			uint32_t generic_ee_cmd_return_code:3;
-			uint32_t resvd2:7;
+			uint32_t resvd2:2;
+			uint32_t generic_ee_cmd_return_val:3;
+			uint32_t resvd4:2;
 			uint32_t max_usb_pkt_size:1;
 			uint32_t resvd3:8;
 			uint32_t mhi_base_chan_idx:8;
@@ -1523,6 +1525,7 @@ enum gsi_generic_ee_cmd_opcode {
 	GSI_GEN_EE_CMD_ALLOC_CHANNEL = 0x2,
 	GSI_GEN_EE_CMD_ENABLE_FLOW_CHANNEL = 0x3,
 	GSI_GEN_EE_CMD_DISABLE_FLOW_CHANNEL = 0x4,
+	GSI_GEN_EE_CMD_QUERY_FLOW_CHANNEL = 0x5,
 };
 
 enum gsi_generic_ee_cmd_return_code {
@@ -1535,6 +1538,11 @@ enum gsi_generic_ee_cmd_return_code {
 	GSI_GEN_EE_CMD_RETURN_CODE_OUT_OF_RESOURCES = 0x7,
 };
 
+enum gsi_generic_ee_cmd_query_retun_val {
+	GSI_GEN_EE_CMD_RETURN_VAL_FLOW_CONTROL_PRIMARY = 0,
+	GSI_GEN_EE_CMD_RETURN_VAL_FLOW_CONTROL_SECONDARY = 1,
+	GSI_GEN_EE_CMD_RETURN_VAL_FLOW_CONTROL_PENDING = 2,
+};
 extern struct gsi_ctx *gsi_ctx;
 
 /**
@@ -2269,6 +2277,10 @@ int gsi_query_aqc_msi_addr(unsigned long chan_hdl, phys_addr_t *addr);
 */
 void gsi_dump_ch_info(unsigned long chan_hdl);
 
+int gsi_flow_control_ee(unsigned int chan_idx, unsigned int ee,
+				bool enable, bool prmy_scnd_fc, int *code);
+int gsi_query_flow_control_state_ee(unsigned int chan_idx, unsigned int ee,
+						bool prmy_scnd_fc, int *code);
 /*
  * Here is a typical sequence of calls
  *

+ 13 - 0
drivers/platform/msm/gsi/gsihal/gsihal_reg.c

@@ -60,6 +60,7 @@ static const char *gsireg_name_to_str[GSI_REG_MAX] = {
 	__stringify(GSI_EE_n_CNTXT_MSI_BASE_MSB),
 	__stringify(GSI_EE_n_GSI_STATUS),
 	__stringify(GSI_EE_n_CNTXT_SCRATCH_0),
+	__stringify(GSI_EE_n_CNTXT_SCRATCH_1),
 	__stringify(GSI_EE_n_EV_CH_k_CNTXT_1),
 	__stringify(GSI_EE_n_EV_CH_k_CNTXT_2),
 	__stringify(GSI_EE_n_EV_CH_k_CNTXT_3),
@@ -1068,6 +1069,9 @@ static void gsireg_construct_gsi_ee_generic_cmd_v3_0(enum gsihal_reg_name reg,
 	GSI_SETFIELD_IN_REG(*val, cmd->ee,
 		GSI_V3_0_EE_n_GSI_EE_GENERIC_CMD_EE_SHFT,
 		GSI_V3_0_EE_n_GSI_EE_GENERIC_CMD_EE_BMSK);
+	GSI_SETFIELD_IN_REG(*val, cmd->prmy_scnd_fc,
+		GSI_V3_0_EE_n_GSI_EE_GENERIC_CMD_PARAM_SHFT,
+		GSI_V3_0_EE_n_GSI_EE_GENERIC_CMD_PARAM_BMSK);
 }
 
 static void gsireg_construct_cntxt_gsi_irq_en(enum gsihal_reg_name reg,
@@ -1205,6 +1209,9 @@ static struct gsihal_reg_obj gsihal_reg_objs[GSI_VER_MAX][GSI_REG_MAX] = {
 	[GSI_VER_1_0][GSI_EE_n_CNTXT_SCRATCH_0] = {
 	gsireg_construct_dummy, gsireg_parse_dummy,
 	0x0001f400, 0x4000, 0},
+	[GSI_VER_1_0][GSI_EE_n_CNTXT_SCRATCH_1] = {
+	gsireg_construct_dummy, gsireg_parse_dummy,
+	0x0001f404, 0x4000, 0},
 	[GSI_VER_1_0][GSI_EE_n_EV_CH_k_CNTXT_1] = {
 	gsireg_construct_ev_ch_k_cntxt_1, gsireg_parse_dummy,
 	0x0001d004, 0x4000, 0x80},
@@ -1554,6 +1561,9 @@ static struct gsihal_reg_obj gsihal_reg_objs[GSI_VER_MAX][GSI_REG_MAX] = {
 	[GSI_VER_2_5][GSI_EE_n_CNTXT_SCRATCH_0] = {
 	gsireg_construct_dummy, gsireg_parse_dummy,
 	0x00012400, 0x4000, 0},
+	[GSI_VER_2_5][GSI_EE_n_CNTXT_SCRATCH_1] = {
+	gsireg_construct_dummy, gsireg_parse_dummy,
+	0x00012404, 0x4000, 0},
 	[GSI_VER_2_5][GSI_EE_n_EV_CH_k_CNTXT_1] = {
 	gsireg_construct_ev_ch_k_cntxt_1, gsireg_parse_dummy,
 	0x00010004, 0x4000, 0x80},
@@ -1905,6 +1915,9 @@ static struct gsihal_reg_obj gsihal_reg_objs[GSI_VER_MAX][GSI_REG_MAX] = {
 	[GSI_VER_3_0][GSI_EE_n_CNTXT_SCRATCH_0] = {
 	gsireg_construct_dummy, gsireg_parse_dummy,
 	0x00025400, 0x12000, 0},
+	[GSI_VER_3_0][GSI_EE_n_CNTXT_SCRATCH_1] = {
+	gsireg_construct_dummy, gsireg_parse_dummy,
+	0x00024504, 0x12000, 0},
 	[GSI_VER_3_0][GSI_INTER_EE_n_SRC_GSI_CH_IRQ_k] = {
 	gsireg_construct_dummy, gsireg_parse_dummy,
 	0x0000c018, 0x1000, 0x18 },

+ 3 - 1
drivers/platform/msm/gsi/gsihal/gsihal_reg.h

@@ -50,6 +50,7 @@ enum gsihal_reg_name {
 	GSI_EE_n_CNTXT_MSI_BASE_MSB,
 	GSI_EE_n_GSI_STATUS,
 	GSI_EE_n_CNTXT_SCRATCH_0,
+	GSI_EE_n_CNTXT_SCRATCH_1,
 	GSI_EE_n_EV_CH_k_CNTXT_1,
 	GSI_EE_n_EV_CH_k_CNTXT_2,
 	GSI_EE_n_EV_CH_k_CNTXT_3,
@@ -313,6 +314,7 @@ struct gsihal_reg_gsi_ee_generic_cmd {
 	uint32_t opcode;
 	uint32_t virt_chan_idx;
 	uint32_t ee;
+	bool prmy_scnd_fc;
 };
 
 struct gsihal_reg_gsi_ee_n_cntxt_gsi_irq {
@@ -494,4 +496,4 @@ unsigned long gsihal_get_inst_ram_size(void);
  */
 u32 gsihal_get_glob_irq_en_gp_int1_mask(void);
 
-#endif /* _GSIHAL_REG_H_ */
+#endif /* _GSIHAL_REG_H_ */

+ 3 - 1
drivers/platform/msm/gsi/gsihal/gsihal_reg_i.h

@@ -334,6 +334,8 @@
 #define GSI_V3_0_EE_n_GSI_EE_GENERIC_CMD_VIRT_CHAN_IDX_SHFT 0x5
 #define GSI_V3_0_EE_n_GSI_EE_GENERIC_CMD_EE_BMSK 0x1e000
 #define GSI_V3_0_EE_n_GSI_EE_GENERIC_CMD_EE_SHFT 0xd
+#define GSI_V3_0_EE_n_GSI_EE_GENERIC_CMD_PARAM_BMSK 0xff000000
+#define GSI_V3_0_EE_n_GSI_EE_GENERIC_CMD_PARAM_SHFT 0x18
 
 /* GSI_EE_n_CNTXT_GSI_IRQ_EN */
 #define GSI_EE_n_CNTXT_GSI_IRQ_EN_GSI_MCS_STACK_OVRFLOW_BMSK 0x8
@@ -345,4 +347,4 @@
 #define GSI_EE_n_CNTXT_GSI_IRQ_EN_GSI_BREAK_POINT_BMSK 0x1
 #define GSI_EE_n_CNTXT_GSI_IRQ_EN_GSI_BREAK_POINT_SHFT 0x0
 
-#endif /* _GSIHAL_REG_I_H_ */
+#endif /* _GSIHAL_REG_I_H_ */

+ 54 - 2
drivers/platform/msm/ipa/ipa_v3/ipa.c

@@ -3669,6 +3669,40 @@ static void ipa3_destroy_imm(void *user1, int user2)
 	ipahal_destroy_imm_cmd(user1);
 }
 
+static void ipa3_q6_pipe_flow_control(bool delay)
+{
+	int ep_idx;
+	int client_idx;
+	int code = 0, result;
+	const struct ipa_gsi_ep_config *gsi_ep_cfg;
+
+	for (client_idx = 0; client_idx < IPA_CLIENT_MAX; client_idx++) {
+		if (IPA_CLIENT_IS_Q6_PROD(client_idx)) {
+			ep_idx = ipa3_get_ep_mapping(client_idx);
+			if (ep_idx == -1)
+				continue;
+			gsi_ep_cfg = ipa3_get_gsi_ep_info(client_idx);
+			if (!gsi_ep_cfg) {
+				IPAERR("failed to get GSI config\n");
+				ipa_assert();
+				return;
+			}
+			IPADBG("pipe setting V2 flow control\n");
+			/* Configurig primary flow control on Q6 pipes*/
+			result = gsi_flow_control_ee(
+					gsi_ep_cfg->ipa_gsi_chan_num,
+					gsi_ep_cfg->ee, delay, false, &code);
+			if (result == GSI_STATUS_SUCCESS) {
+				IPADBG("sussess gsi ch %d with code %d\n",
+					gsi_ep_cfg->ipa_gsi_chan_num, code);
+			} else {
+				IPADBG("failed  gsi ch %d code %d\n",
+					gsi_ep_cfg->ipa_gsi_chan_num, code);
+			}
+		}
+	}
+}
+
 static void ipa3_q6_pipe_delay(bool delay)
 {
 	int client_idx;
@@ -4384,8 +4418,12 @@ void ipa3_q6_pre_shutdown_cleanup(void)
 	IPA_ACTIVE_CLIENTS_INC_SIMPLE();
 
 	ipa3_update_ssr_state(true);
-	if (!ipa3_ctx->ipa_endp_delay_wa)
+
+	if (ipa3_ctx->ipa_endp_delay_wa_v2)
+		ipa3_q6_pipe_flow_control(true);
+	else if (!ipa3_ctx->ipa_endp_delay_wa)
 		ipa3_q6_pipe_delay(true);
+
 	ipa3_q6_avoid_holb();
 	if (ipa3_ctx->ipa_config_is_mhi) {
 		ipa3_set_reset_client_cons_pipe_sus_holb(true,
@@ -4415,7 +4453,11 @@ void ipa3_q6_pre_shutdown_cleanup(void)
 	 * on pipe reset procedure
 	 */
 
-	if (!ipa3_ctx->ipa_endp_delay_wa) {
+	if (ipa3_ctx->ipa_endp_delay_wa_v2) {
+		ipa3_q6_pipe_flow_control(false);
+		ipa3_set_reset_client_prod_pipe_delay(true,
+			IPA_CLIENT_USB_PROD);
+	} else if (!ipa3_ctx->ipa_endp_delay_wa) {
 		ipa3_q6_pipe_delay(false);
 		ipa3_set_reset_client_prod_pipe_delay(true,
 			IPA_CLIENT_USB_PROD);
@@ -7670,6 +7712,7 @@ static int ipa3_pre_init(const struct ipa3_plat_drv_res *resource_p,
 		ipa3_ctx->do_testbus_collection_on_crash = false;
 	}
 	ipa3_ctx->ipa_endp_delay_wa = resource_p->ipa_endp_delay_wa;
+	ipa3_ctx->ipa_endp_delay_wa_v2 = resource_p->ipa_endp_delay_wa_v2;
 
 	WARN(ipa3_ctx->ipa3_hw_mode != IPA_HW_MODE_NORMAL,
 		"Non NORMAL IPA HW mode, is this emulation platform ?");
@@ -8407,6 +8450,7 @@ static int get_ipa_dts_configuration(struct platform_device *pdev,
 	ipa_drv_res->ipa_gpi_event_rp_ddr = false;
 	ipa_drv_res->ipa_config_is_auto = false;
 	ipa_drv_res->max_num_smmu_cb = IPA_SMMU_CB_MAX;
+	ipa_drv_res->ipa_endp_delay_wa_v2 = false;
 
 	/* Get IPA HW Version */
 	result = of_property_read_u32(pdev->dev.of_node, "qcom,ipa-hw-ver",
@@ -8499,6 +8543,14 @@ static int get_ipa_dts_configuration(struct platform_device *pdev,
 			ipa_drv_res->ipa_endp_delay_wa
 			? "True" : "False");
 
+	ipa_drv_res->ipa_endp_delay_wa_v2 =
+			of_property_read_bool(pdev->dev.of_node,
+			"qcom,ipa-endp-delay-wa-v2");
+	IPADBG(": endppoint delay wa v2 = %s\n",
+			ipa_drv_res->ipa_endp_delay_wa_v2
+			? "True" : "False");
+
+
 	ipa_drv_res->ipa_wdi3_over_gsi =
 			of_property_read_bool(pdev->dev.of_node,
 			"qcom,ipa-wdi3-over-gsi");

+ 32 - 4
drivers/platform/msm/ipa/ipa_v3/ipa_client.c

@@ -1128,6 +1128,29 @@ static int ipa3_xdci_stop_gsi_ch_brute_force(u32 clnt_hdl,
 	}
 }
 
+int ipa3_remove_secondary_flow_ctrl(int gsi_chan_hdl)
+{
+	int code = 0;
+	int result;
+
+	result = gsi_query_flow_control_state_ee(gsi_chan_hdl, 0, 1, &code);
+	if (result == GSI_STATUS_SUCCESS) {
+		code = 0;
+		result = gsi_flow_control_ee(gsi_chan_hdl, 0, false, true,
+							&code);
+		if (result == GSI_STATUS_SUCCESS) {
+			IPADBG("flow control sussess ch %d code %d\n",
+					gsi_chan_hdl, code);
+		} else {
+			IPADBG("failed to flow control ch %d code %d\n",
+					gsi_chan_hdl, code);
+		}
+	} else {
+		IPADBG("failed to query flow control mode ch %d code %d\n",
+					gsi_chan_hdl, code);
+	}
+	return result;
+}
 /* Clocks should be voted for before invoking this function */
 static int ipa3_stop_ul_chan_with_data_drain(u32 qmi_req_id,
 		u32 source_pipe_bitmask, u32 source_pipe_reg_idx,
@@ -1213,10 +1236,15 @@ static int ipa3_stop_ul_chan_with_data_drain(u32 qmi_req_id,
 			IPAERR(
 				"failed to force clear %d, remove delay from SCND reg\n"
 				, result);
-			ep_ctrl_scnd.endp_delay = false;
-			ipahal_write_reg_n_fields(
-				IPA_ENDP_INIT_CTRL_SCND_n, clnt_hdl,
-				&ep_ctrl_scnd);
+			if (ipa3_ctx->ipa_endp_delay_wa_v2) {
+				ipa3_remove_secondary_flow_ctrl(
+						ep->gsi_chan_hdl);
+			} else {
+				ep_ctrl_scnd.endp_delay = false;
+				ipahal_write_reg_n_fields(
+					IPA_ENDP_INIT_CTRL_SCND_n, clnt_hdl,
+					&ep_ctrl_scnd);
+			}
 		}
 	}
 	/* with force clear, wait for emptiness */

+ 3 - 0
drivers/platform/msm/ipa/ipa_v3/ipa_i.h

@@ -2199,6 +2199,7 @@ struct ipa3_context {
 	u32 ipa_wdi3_2g_holb_timeout;
 	u32 ipa_wdi3_5g_holb_timeout;
 	bool is_wdi3_tx1_needed;
+	bool ipa_endp_delay_wa_v2;
 };
 
 struct ipa3_plat_drv_res {
@@ -2270,6 +2271,7 @@ struct ipa3_plat_drv_res {
 	u32 max_num_smmu_cb;
 	u32 ipa_wdi3_2g_holb_timeout;
 	u32 ipa_wdi3_5g_holb_timeout;
+	bool ipa_endp_delay_wa_v2;
 };
 
 /**
@@ -2978,6 +2980,7 @@ void ipa3_skb_recycle(struct sk_buff *skb);
 void ipa3_install_dflt_flt_rules(u32 ipa_ep_idx);
 void ipa3_delete_dflt_flt_rules(u32 ipa_ep_idx);
 
+int ipa3_remove_secondary_flow_ctrl(int gsi_chan_hdl);
 int ipa3_enable_data_path(u32 clnt_hdl);
 int ipa3_disable_data_path(u32 clnt_hdl);
 int ipa3_disable_gsi_data_path(u32 clnt_hdl);

+ 23 - 12
drivers/platform/msm/ipa/ipa_v3/ipa_uc_wdi.c

@@ -1412,12 +1412,6 @@ int ipa3_connect_gsi_wdi_pipe(struct ipa_wdi_in_params *in,
 	ep->skip_ep_cfg = in->sys.skip_ep_cfg;
 	ep->client_notify = in->sys.notify;
 	ep->priv = in->sys.priv;
-	if (IPA_CLIENT_IS_PROD(in->sys.client)) {
-		memset(&ep_cfg_ctrl, 0, sizeof(struct ipa_ep_cfg_ctrl));
-		ep_cfg_ctrl.ipa_ep_delay = true;
-		ipa3_cfg_ep_ctrl(ipa_ep_idx, &ep_cfg_ctrl);
-	}
-
 	if (IPA_CLIENT_IS_CONS(in->sys.client)) {
 		in->sys.ipa_ep_cfg.aggr.aggr_en = IPA_ENABLE_AGGR;
 		in->sys.ipa_ep_cfg.aggr.aggr = IPA_GENERIC;
@@ -1454,6 +1448,13 @@ int ipa3_connect_gsi_wdi_pipe(struct ipa_wdi_in_params *in,
 				&ep->gsi_chan_hdl, ep->gsi_evt_ring_hdl);
 	if (result)
 		goto fail_alloc_channel;
+
+	if (IPA_CLIENT_IS_PROD(in->sys.client)) {
+		memset(&ep_cfg_ctrl, 0, sizeof(struct ipa_ep_cfg_ctrl));
+		ep_cfg_ctrl.ipa_ep_delay = true;
+		ipa3_cfg_ep_ctrl(ipa_ep_idx, &ep_cfg_ctrl);
+	}
+
 	ep->gsi_mem_info.chan_ring_len = gsi_channel_props.ring_len;
 	ep->gsi_mem_info.chan_ring_base_addr = gsi_channel_props.ring_base_addr;
 	ep->gsi_mem_info.chan_ring_base_vaddr =
@@ -2670,10 +2671,15 @@ int ipa3_suspend_gsi_wdi_pipe(u32 clnt_hdl)
 				 */
 				IPAERR("failed to force clear %d\n", res);
 				IPAERR("remove delay from SCND reg\n");
-				ep_ctrl_scnd.endp_delay = false;
-				ipahal_write_reg_n_fields(
-					IPA_ENDP_INIT_CTRL_SCND_n, clnt_hdl,
-						&ep_ctrl_scnd);
+				if (ipa3_ctx->ipa_endp_delay_wa_v2) {
+					ipa3_remove_secondary_flow_ctrl(
+							ep->gsi_chan_hdl);
+				} else {
+					ep_ctrl_scnd.endp_delay = false;
+					ipahal_write_reg_n_fields(
+						IPA_ENDP_INIT_CTRL_SCND_n,
+						clnt_hdl, &ep_ctrl_scnd);
+				}
 			} else {
 				disable_force_clear = true;
 			}
@@ -2799,10 +2805,15 @@ int ipa3_suspend_wdi_pipe(u32 clnt_hdl)
 				 */
 				IPAERR("failed to force clear %d\n", result);
 				IPAERR("remove delay from SCND reg\n");
-				ep_ctrl_scnd.endp_delay = false;
-				ipahal_write_reg_n_fields(
+				if (ipa3_ctx->ipa_endp_delay_wa_v2) {
+					ipa3_remove_secondary_flow_ctrl(
+							ep->gsi_chan_hdl);
+				} else {
+					ep_ctrl_scnd.endp_delay = false;
+					ipahal_write_reg_n_fields(
 					IPA_ENDP_INIT_CTRL_SCND_n, clnt_hdl,
 					&ep_ctrl_scnd);
+				}
 			} else {
 				disable_force_clear = true;
 			}

+ 31 - 0
drivers/platform/msm/ipa/ipa_v3/ipa_utils.c

@@ -7401,6 +7401,10 @@ int ipa3_cfg_ep_hdr_ext(u32 clnt_hdl,
  */
 int ipa3_cfg_ep_ctrl(u32 clnt_hdl, const struct ipa_ep_cfg_ctrl *ep_ctrl)
 {
+	int code = 0, result;
+	struct ipa3_ep_context *ep;
+	bool primary_secondry;
+
 	if (clnt_hdl >= ipa3_ctx->ipa_num_pipes || ep_ctrl == NULL) {
 		IPAERR("bad parm, clnt_hdl = %d\n", clnt_hdl);
 		return -EINVAL;
@@ -7421,6 +7425,33 @@ int ipa3_cfg_ep_ctrl(u32 clnt_hdl, const struct ipa_ep_cfg_ctrl *ep_ctrl)
 		clnt_hdl,
 		ep_ctrl->ipa_ep_suspend,
 		ep_ctrl->ipa_ep_delay);
+	ep = &ipa3_ctx->ep[clnt_hdl];
+	if (ipa3_ctx->ipa_endp_delay_wa_v2 &&
+		IPA_CLIENT_IS_PROD(ep->client)) {
+
+		IPADBG("Configuring flow control for pipe = %d\n", clnt_hdl);
+		/* Configure enhanced flow control instead of delay
+		 * Q6 controlled AP pipes(USB PROD and MHI_PROD) configuring the
+		 * secondary flow control.
+		 * AP controlled pipe configuring primary flow control.
+		 */
+		if (ep->client == IPA_CLIENT_USB_PROD ||
+			ep->client == IPA_CLIENT_MHI_PROD)
+			primary_secondry = true;
+		else
+			primary_secondry = false;
+
+		result = gsi_flow_control_ee(ep->gsi_chan_hdl, 0,
+				ep_ctrl->ipa_ep_delay, primary_secondry, &code);
+		if (result == GSI_STATUS_SUCCESS) {
+			IPADBG("flow control sussess gsi ch %d with code %d\n",
+					ep->gsi_chan_hdl, code);
+		} else {
+			IPADBG("failed to flow control gsi ch %d code %d\n",
+					ep->gsi_chan_hdl, code);
+		}
+		return 0;
+	}
 
 	ipahal_write_reg_n_fields(IPA_ENDP_INIT_CTRL_n, clnt_hdl, ep_ctrl);
 

+ 10 - 4
drivers/platform/msm/ipa/ipa_v3/ipa_wdi3_i.c

@@ -1090,10 +1090,16 @@ int ipa3_disable_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx,
 			 */
 			IPAERR("failed to force clear %d\n", result);
 			IPAERR("remove delay from SCND reg\n");
-			ep_ctrl_scnd.endp_delay = false;
-			ipahal_write_reg_n_fields(
-				IPA_ENDP_INIT_CTRL_SCND_n, ipa_ep_idx_rx,
-					&ep_ctrl_scnd);
+			if (ipa3_ctx->ipa_endp_delay_wa_v2) {
+				ipa3_remove_secondary_flow_ctrl(
+							ep->gsi_chan_hdl);
+			} else {
+				ep_ctrl_scnd.endp_delay = false;
+				ipahal_write_reg_n_fields(
+						IPA_ENDP_INIT_CTRL_SCND_n,
+						ipa_ep_idx_rx,
+						&ep_ctrl_scnd);
+			}
 		} else {
 			disable_force_clear = true;
 		}

+ 10 - 4
drivers/platform/msm/ipa/ipa_v3/ipa_wigig_i.c

@@ -1822,10 +1822,16 @@ int ipa3_disable_wigig_pipe_i(enum ipa_client_type client)
 			 */
 			IPAERR("failed to force clear %d\n", res);
 			IPAERR("remove delay from SCND reg\n");
-			ep_ctrl_scnd.endp_delay = false;
-			ipahal_write_reg_n_fields(
-					IPA_ENDP_INIT_CTRL_SCND_n, ipa_ep_idx,
-					&ep_ctrl_scnd);
+			if (ipa3_ctx->ipa_endp_delay_wa_v2) {
+				ipa3_remove_secondary_flow_ctrl(
+							ep->gsi_chan_hdl);
+			} else {
+				ep_ctrl_scnd.endp_delay = false;
+				ipahal_write_reg_n_fields(
+						IPA_ENDP_INIT_CTRL_SCND_n,
+						ipa_ep_idx,
+						&ep_ctrl_scnd);
+			}
 		} else {
 			disable_force_clear = true;
 		}